#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <getopt.h>
#include <sqlite3.h>

#include <string>
#include <memory>
#include <vector>
#include <utility>

#include "sym-name-table.h"
#include "database.h"
#include "string-util.h"
#include "autocall-util.h"

using std::vector;
using std::string;
using std::shared_ptr;
using std::dynamic_pointer_cast;
using std::runtime_error;
using std::pair;

/*
 * C++ 中的符号名,就像路径一样;如:'::std::gnu::open_v2',只不过把路径使用的'/'换成了
 * '::'.
 * qualifier_;存放着符号名的限定部分;若其以'::'开头,则表明是 absolute 的,即此时唯一
 *  确定了一个符号;若不是以'::'开头,则是相对的.如:'gnu::open_v2'可能位于命名空间 std
 *  中,也可能位于另外一个命名空间!
 * sym_name_; 存放着符号名部分.
 */
struct CPPSymName : public SymName {
    friend class CPPSymNameTable;

    const std::string& language() override;
    const std::string& name() override;

    /**
     * 解析 src 提取出 qualifier_,sym_name_ 部分.
     * @param src 要求: 是合法的 C++ 符号名.
     * @param size 若小于 0,则 size = strlen(src).
     */
    void parse(const char *src,int size = -1);

    /// 测试当前 CPPSymName 是否是 absolute 的.
    bool isAbsolute() const
    {
        return qualifier_[0] == ':';
    }

    /// 若当前 C++ 符号名有限定符,则返回真;
    bool hasQualifier() const
    {
        return !qualifier_.empty();
    }

    static const string LANGUAGE;
private:
    // 总不会追加末尾的'::'.
    string qualifier_;
    string sym_name_;

    // 由 qualifier_ 与 sym_name_ 拼接而成的完全名称,专为 name() 使用.
    // 我讨厌 name() 这个接口的设计! std::string name() 多好!
    string full_sym_name_;
};


const string CPPSymName::LANGUAGE {"C++"};

const std::string& CPPSymName::language()
{
    return LANGUAGE;
}

const std::string& CPPSymName::name()
{
    if (!hasQualifier()) {
        full_sym_name_ = sym_name_;
    } else {
        full_sym_name_ = qualifier_;
        if (qualifier_ != "::")
            full_sym_name_.append("::");
        full_sym_name_ += sym_name_;
    }
    return full_sym_name_;
}

void CPPSymName::parse(const char *src,int size )
{
    if (size < 0)
        size = strlen(src);

    const char *colon_ptr = static_cast<const char*>(memrchr(src,':',size));
    if (colon_ptr == NULL) {
        qualifier_.clear();
        sym_name_.assign(src,size);
    } else {
        --colon_ptr;

        if (colon_ptr == src)
            qualifier_ = "::";
        else
            qualifier_.assign(src,colon_ptr - src);

        const char * const end = src + size;
        const char * sym_name_ptr = colon_ptr + 2;
        sym_name_.assign(sym_name_ptr,end - sym_name_ptr);
    }
    return ;
}

namespace {

// 测试 ch 是否是 'a'-'z' 或者 'A'-'Z'.
bool isazAZ(char ch)
{
    return (ch <= 'z' && ch >= 'a') ||
           (ch <= 'Z' && ch >= 'A');
}


/*
 * 检查 [src,src + size) 是否是一个合法的 C 符号名.即由下划线,字母开始的下划线,字母,数
 * 字组合.若不是,则返回假;否则,返回真.
 * @param size 若小于 0,则 size = strlen(src);
 *  若等于 0,则返回假,因为很显然空串并不是一个合法的符号名.
 */
bool check_c_sym_name(const char *src,int size = -1)
{
    if (size < 0)
        size = strlen(src);
    if (size == 0)
        return false;

    const char * const src_end = src + size;
    if (*src == '_' || isazAZ(*src)) {
        for (++src; src < src_end; ++src) {
            char ch = *src;
            if (!(ch == '_' || isdigit(ch) || isazAZ(ch)))
                return false;
        }
    } else {
        return false;
    }
    return true;
}

/*
 * 检查 [ptr,ptr + size) 是否是一个合法的 C++ 符号名;若是,则返回真;否则,返回假.
 * 一个合法的 C++ 符号名被认为是'[::]VALID_C_SYM_NAME[::VALID_C_SYM_NAME]
 * @param size 若小于 0,则 size = strlen(src);
 *  若等于 0,则返回假,因为很显然空串并不是一个合法的符号名.
 */
bool check_cpp_sym_name(const char *ptr,int size = -1)
{
    if (size < 0)
        size = strlen(ptr);
    if (size == 0)
        return false;
    const char * const end = ptr + size;

    if (ptr[0] == ':') {
        auto ptr1 = ptr + 1;
        if (ptr1 >= end)
            return false;
        if (ptr1[0] != ':')
            return false;
        auto ptr2 = ptr1 + 1;
        if (ptr2 >= end)
            return false;
        if (*ptr2 == ':')
            return false;
        ptr = ptr2;
    }
    while (ptr < end) {
        const size_t left_size = end - ptr;
        auto colon_ptr = static_cast<const char*>(memchr(ptr,':',left_size));
        if (colon_ptr == NULL)
            return check_c_sym_name(ptr,left_size);
        {
            auto ptr1 = colon_ptr + 1;
            if (ptr1 >= end)
                return false;
            if (ptr1[0] != ':')
                return false;
            auto ptr2 = ptr1 + 1;
            if (ptr2 >= end)
                return false;
            if (*ptr2 == ':')
                return false;
        }
        if (!check_c_sym_name(ptr,colon_ptr - ptr))
            return false;
        ptr = colon_ptr + 2;
    }

    return true;
}


} // namespace

struct CPPSymNameTable : public SymNameTable {
    const std::string& help() override;

    const std::string& language() override;

    std::shared_ptr<SymName> getSymName(int argc,char **argv) override;

    std::vector<std::pair<SymInfoTable::id_t,std::shared_ptr<SymName>>>
    find(std::shared_ptr<SymName>) override ;

    void add(SymInfoTable::id_t,std::shared_ptr<SymName>) override;

    // 在构造函数中完成表的创建.
    CPPSymNameTable();


private:
    static const char *table_name;
    static const string help_doc;
};

const string CPPSymNameTable::help_doc
{
    "符号名必须放在命令行参数中最后一个非选项参数!"
};

const char * CPPSymNameTable::table_name = "cpp_sym_name_table";

const std::string& CPPSymNameTable::help()
{
    return help_doc;
}

const std::string& CPPSymNameTable::language()
{
    return CPPSymName::LANGUAGE;
}

std::shared_ptr<SymName> CPPSymNameTable::getSymName(int argc,char **argv)
{
    const char *sym_name = NULL;

    int getopt_ret;
    optind = 0;
    opterr = 0;
    while ((getopt_ret = getopt(argc,argv,"-")) != -1) {
        switch (getopt_ret) {
        case 1:
            sym_name = optarg;
            break;
        default:
            break;
        }
    }
    if (sym_name == NULL)
        throw runtime_error("CPP-LANGUAGE: 未指定符号名!");

    if (!check_cpp_sym_name(sym_name))
        throw runtime_error(format("CPP-LANGUAGE: %s 不是合法的 C++ 标识符!",sym_name));

    CPPSymName *cpp_sym_name = new CPPSymName;
    cpp_sym_name->parse(sym_name);
//    LOGD("q: %s;s: %s;n: %s",cpp_sym_name->qualifier_.c_str(),cpp_sym_name->sym_name_.c_str(),cpp_sym_name->name().c_str());
    return shared_ptr<SymName>{cpp_sym_name};
}

CPPSymNameTable::CPPSymNameTable()
{
    /*
     * 表的结构;
     * qualifier;存放着符号的完全限定符,总是以'::'开头的.
     * sym_name;存放着符号名.
     * 很显然 qualifier,sym_name 应该是唯一的!
     */
    auto conn_ptr = database_connection();
    string sql_stmt_str = format(
        "CREATE TABLE IF NOT EXISTS  %s ("
        "sym_info_id INT UNIQUE NOT NULL,"
        "qualifier BLOB NOT NULL,"
        "sym_name BLOB NOT NULL,"
        "UNIQUE (qualifier,sym_name) )",table_name);
    exec_sql(conn_ptr,sql_stmt_str.c_str());
    return ;
}

vector<pair<SymInfoTable::id_t,shared_ptr<SymName>>>
CPPSymNameTable::find(std::shared_ptr<SymName> s)
{
    shared_ptr<CPPSymName> cpp_symname = dynamic_pointer_cast<CPPSymName>(s);
    if (!cpp_symname)
        throw runtime_error("CPP-LANGUAGES.add(): 参数不合法!");

    vector<pair<SymInfoTable::id_t,shared_ptr<SymName>>> result_vec;

    // 构造 SQL 语句.
    string sql_stmt_str = format(
                "SELECT sym_info_id,qualifier FROM %s WHERE sym_name = '%s' ",
                table_name,cpp_symname->sym_name_.c_str());
    if (cpp_symname->hasQualifier()) {
        if (cpp_symname->isAbsolute()) {
            format(sql_stmt_str,"AND qualifier = '%s'",
                   cpp_symname->qualifier_.c_str());
        } else {
            format(sql_stmt_str,"AND qualifier GLOB '*::%s'",
                   cpp_symname->qualifier_.c_str());
        }
    }

    // prepare sql 语句
    sqlite3_stmt *sql_stmt_ptr = NULL;
    auto conn_ptr = database_connection();
    int prepare_ret = sqlite3_prepare(conn_ptr,sql_stmt_str.c_str(),-1,&sql_stmt_ptr,NULL);
    if (prepare_ret != SQLITE_OK) {
        throw runtime_error(format("sql语句 %s prepare 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(conn_ptr)));
    }
    AUTOCALL_FUNC(destroy_prepare_stmt) {
        if (sql_stmt_ptr != NULL)
            sqlite3_finalize(sql_stmt_ptr);
    };
    AUTOCALL(destroy_prepare_stmt);

    // 执行 sql 语句,并获取结果.
    int step_ret;
    while ((step_ret = sqlite3_step(sql_stmt_ptr)) == SQLITE_ROW) {
        pair<SymInfoTable::id_t,shared_ptr<SymName>> one_result;
        one_result.first = sqlite3_column_int64(sql_stmt_ptr,0);
        int qualifier_size = sqlite3_column_bytes(sql_stmt_ptr,1);
        const char *qualifier_ptr = static_cast<const char*>(sqlite3_column_blob(sql_stmt_ptr,1));
        if (qualifier_ptr == NULL || qualifier_size == 0)
            throw runtime_error("CPP-LANGUAGE.find():error");
        CPPSymName *one_symname = new CPPSymName;
        one_symname->sym_name_ = cpp_symname->sym_name_;
        one_symname->qualifier_.assign(qualifier_ptr,qualifier_size);
        one_result.second.reset(one_symname);
        result_vec.push_back(one_result);
    }
    if (step_ret != SQLITE_DONE) {
        throw runtime_error(format("sql语句 %s step 出错;出错原因: %s",
                                   sql_stmt_str.c_str(),
                                   sqlite3_errmsg(conn_ptr)));
    }

    return result_vec;
}

void CPPSymNameTable::add(SymInfoTable::id_t id,std::shared_ptr<SymName> s)
{
    auto cpp_symname = dynamic_pointer_cast<CPPSymName>(s);
    if (!cpp_symname)
        throw runtime_error("CPP-LANGUAGES.add(): 参数不合法!");

    if (!cpp_symname->isAbsolute()) {
        throw runtime_error(format(
            "CPP-LANGUAGE.add: %s 不是完全限定的!为了避免二义性,符号名应该总以'::'开始!",
            cpp_symname->name().c_str()));
    }

    string sql_stmt_str = format(
        "INSERT INTO %s (sym_info_id,qualifier,sym_name) VALUES (%lld,'%s','%s')",
        table_name,id,
        cpp_symname->qualifier_.c_str(),
        cpp_symname->sym_name_.c_str());
    auto conn_ptr = database_connection();
    exec_sql(conn_ptr,sql_stmt_str.c_str());
    return ;
}


LANGUAGE_REGISTER(CPPSymName::LANGUAGE,CPPSymNameTable);
